package cn.imokay.juc.future;

import lombok.Getter;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public class CompletableFutureDemo {
    public static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("tianmao"),
            new NetMall("vip"),
            new NetMall("dangdang"),
            new NetMall("pdd"));

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1();
        //
        long start = System.currentTimeMillis();

        List<String> prices = getPriceByProd(list, "mysql");
        prices.stream().forEach(System.out::println);
        long end = System.currentTimeMillis();
        System.out.println(end - start);




        start = System.currentTimeMillis();
        prices = getPriceByProdThroughCompletableFuture(list, "mysql");
        prices.stream().forEach(System.out::println);
        end = System.currentTimeMillis();
        System.out.println(end - start);
        //test2();

    }

    public static List<String> getPriceByProd(List<NetMall> netMalls,String prodName){
        return netMalls.stream().
                map(netMall -> String.format("%s price in %s is %2f",
                                prodName,
                                netMall.getNetMallName(),
                                netMall.getPrice(prodName)))
                        .collect(Collectors.toList());
    }

    public static List<String> getPriceByProdThroughCompletableFuture(List<NetMall> netMalls, String prodName) {
        return netMalls.stream()
                        .map(netMall ->
                            CompletableFuture.supplyAsync(() ->
                                String.format("%s price in %s is %2f",
                                prodName,
                                netMall.getNetMallName(),
                                netMall.getPrice(prodName))))
                //stream流具有惰性计算的特点，需要收集一次触发计算的执行，否则直接map，则还是一个串行的结果
                        .collect(Collectors.toList())
                        .stream()
                        .map(completableFuture -> completableFuture.join())
                        .collect(Collectors.toList());

    }

    private static void test2() {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            //用默认线程池时，创建的线程都是守护线程，所以要静态方法请传入自定义线程池，
            // 否则外面主线程执行完时，不等这里执行完就会强制退出虚拟机
            System.out.println("是否守护线程："+Thread.currentThread().isDaemon());
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            int i1 = new Random().nextInt(10);
            if (i1>5) {
                System.out.println("结果有问题，即将跑错："+i1);
                i1 = 1/0;
            }
            return i1;
        },pool).whenComplete((v,e)->{
            //当完成时调用
            if (e==null) {
                System.out.println("任务结束，获得结果："+v);
            }else {
                System.out.println("任务异常结束，获得结果："+v);
            }
        }).exceptionally(e -> {
            //异常时调用
            e.printStackTrace();
            return 500;
        });

        System.out.println(Thread.currentThread().getName()+"线程执行结束");
        //这里已经不需要通过get则合格阻塞方法去获取结果，因为回调函数中已经能拿到结果进行处理
        //future2.get();
        pool.shutdown();
    }

    //普通future能做的，CompletableFuture全都能做
    private static void test1() throws InterruptedException, ExecutionException {
        //可有第二个参数，指定线程池，如果不传就用默认的
        //无返回值的
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
        });
        System.out.println(future.get());//null

        //有返回值
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "sdgsdg";
        });
        System.out.println(future2.get());
    }

}

class NetMall{
    @Getter
    private String netMallName;
    public NetMall(String netMallName) {
        this.netMallName = netMallName;
    }

    //模拟网络请求查询价格的方法
    public double getPrice(String prodName){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return ThreadLocalRandom.current().nextDouble()*2 + prodName.charAt(0);

    }
}
